/*
 * Copyright (c) 2021 KaiHong Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_gpio.h"
#include "iot_pwm.h"
#include "iot_watchdog.h"

#include "oled_ssd1306.h"
#include "snake.h"

#define IOT_IO_NAME_GPIO_5 5 // for oled button A
#define IOT_IO_NAME_GPIO_8 8 // for button B
//#define IOT_IO_NAME_GPIO_8 8
#define SNAKE_REFRESH_TIME 90   //刷新参数
#define SNAKE_WAIT_TIME 2

snakeType Snake;
snakeDirection buttonPressed = DIREC_STRAIGHT;
FoodType Food;

/*开始界面，选择难度 A选择 B确认开始游戏*/
static void GameMenu(void)
{
    gameLevel level;

    OledFillScreen(0x00);
    OledShowString(28, 1, "Game Snake", FONT8x16);
    
    OledShowString(18, 3, "-> Level 1", FONT6x8);
    OledShowString(18, 4, "   Level 2", FONT6x8);
    OledShowString(18, 5, "   Level 3", FONT6x8);
    OledShowString(4, 6, "A-level B-game", FONT6x8);
    
    level = LEVEL_EASY;  //默认简单难度
    while (1){
        if (buttonPressed == DIREC_LEFT){
             
              if (level == LEVEL_HARD){
                  OledShowString(18, 5, "  ", FONT6x8);
                  OledShowString(18, 3, "->", FONT6x8);
                  level = LEVEL_EASY;
              } else {
                  OledShowString(18, LEVEL_NOMARL+level, "  ", FONT6x8);
                  level++;
                  OledShowString(18, LEVEL_NOMARL+level, "->", FONT6x8);
              }
              buttonPressed = DIREC_STRAIGHT;
              
        }
        if (buttonPressed == DIREC_RIGHT){
            Snake.Level = level;
            buttonPressed = DIREC_STRAIGHT;
            break;
        }
        osDelay(SNAKE_WAIT_TIME);
    }
    //OledShowString(32, 5, "    ", 1);
}
/*初始化游戏*/
static void InitSnake(void)
{
    uint8 i = 0;
    Snake.Long = 3; //定义初始化蛇的长度
    Snake.Direction = DIREC_RIGHT; //初始向右行进
    Snake.Level = LEVEL_NOMARL;
    Food.Yes = 1;

    GameMenu();
    OledFillScreen(0x00);
    for (i = 0; i < Snake.Long; i++)
    { //将蛇赋值
        Snake.X[i] = i;
        Snake.Y[i] = 1;
        PrintBlock(Snake.X[i], Snake.Y[i]);
    }
}

/* 判断蛇是否撞墙或者咬到自己 */
static boolean isSnakeDead(void)
{
    int8 i;

    //判断蛇是否幢左右两边的墙
    if ((Snake.X[Snake.Long - 1] < 0) || (Snake.X[Snake.Long - 1] * 8 > GAME_MAX_WIDTH))
    {
        return TRUE;
    }
    //判断蛇是否幢上下两边的墙
    if ((Snake.Y[Snake.Long - 1] < 0) || ((Snake.Y[Snake.Long - 1] * 8) > GAME_MAX_BOTTON))
    {
        return TRUE;
    }
    for (i = 3; i < Snake.Long; i++)
    { //从第三节开始判断蛇头是否咬到自己
        if (Snake.X[i] == Snake.X[0] && Snake.Y[i] == Snake.Y[0])
        {
            return TRUE;
        }
    }
     
    return FALSE;
}
/*是否吃到食物*/
static boolean EatFood(int8 newPos[2])
{
    if (((Snake.X[Snake.Long - 1] + newPos[0]) == Food.X) && ((Snake.Y[Snake.Long - 1] + newPos[1]) == Food.Y))
    {
        return TRUE;
    }

    return FALSE;
}
/*放置食物*/
static void PutFood(void)
{
    uint8 i = 0;

    if (Food.Yes == 1)
    {
        while (1)
        {
            //随机食物位置
            srand(osKernelGetSysTimerCount());
            Food.X = rand() % 16;
            Food.Y = rand() % 8;
            for (i = 0; i < Snake.Long; i++)
            {
                //判断是否和蛇身重合
                if (Food.X == Snake.X[i] && Food.Y == Snake.Y[i])
                    break;
            }
            if (i == Snake.Long)
            {
                Food.Yes = 0;
                PrintBlock(Food.X, Food.Y);
                break; //产生有效的食物坐标
            }
        }
    }
}
/*蛇的移动*/
static void SnakeMove(void)
{
    uint8 i = 0;
    snakeDirection direc;
    int8 newPos[2] = {0, 0};

    direc = buttonPressed;
    buttonPressed = DIREC_STRAIGHT;
    if (direc == DIREC_LEFT)
    { //左转
        newPos[0] = snakeDirectonInfo[Snake.Direction][2];
        newPos[1] = snakeDirectonInfo[Snake.Direction][3];
        Snake.Direction = (Snake.Direction + 1) > DIREC_BOTTOM ? (DIREC_RIGHT) : (Snake.Direction + 1);//新的方向
    }
    else if (direc == DIREC_RIGHT)
    { //右转
        newPos[0] = snakeDirectonInfo[Snake.Direction][4];
        newPos[1] = snakeDirectonInfo[Snake.Direction][5];
        Snake.Direction = (Snake.Direction - 1) < DIREC_RIGHT ? (DIREC_BOTTOM) : (Snake.Direction - 1);//新的方向
    }
    else
    { //前进
        newPos[0] = snakeDirectonInfo[Snake.Direction][0];
        newPos[1] = snakeDirectonInfo[Snake.Direction][1];
    }

    if (EatFood(newPos))
    {
        Snake.Long++;  //吃到食物 蛇长度+1
        Snake.X[Snake.Long - 1] = Food.X;
        Snake.Y[Snake.Long - 1] = Food.Y;
        Food.Yes = 1;
    }
    else
    {
        DisplayBlock(Snake.X[0], Snake.Y[0]); //未吃到食物，熄灭蛇尾
        for (i = 0; i < Snake.Long - 1; i++)
        {
            //蛇身都是它前一段的蛇身的位置
            Snake.X[i] = Snake.X[i + 1];
            Snake.Y[i] = Snake.Y[i + 1];
        }
        SNAKE_MOVE(newPos[0], newPos[1]);
        PrintBlock(Snake.X[Snake.Long - 1], Snake.Y[Snake.Long - 1]);
    }
}
/*过关界面*/
static void GamePass(void)
{
    snakeDirection btnPressed = DIREC_STRAIGHT;

    OledFillScreen(0x00);
    OledShowString(4, 2, "congratulations!", FONT8x16);
    OledShowString(10, 4, "GAME PASS!", FONT8x16);
    OledShowString(2, 5, "New game for B", FONT6x8);
    while (1)
    { 
        btnPressed = buttonPressed;
        if (btnPressed == DIREC_RIGHT)
        {
            buttonPressed = DIREC_STRAIGHT;
            break;
        }
        osDelay(SNAKE_WAIT_TIME);
    }
    OledFillScreen(0x00);
    InitSnake();
}
/*未正常过关界面*/
static void GameOver(void)
{
    snakeDirection btnPressed = DIREC_STRAIGHT;
    OledFillScreen(0x00);
    OledShowString(26, 2, "GAME OVER", FONT8x16);
    OledShowString(1, 4, "New game for B", FONT6x8);
    while (1)
    {
        btnPressed = buttonPressed;
        
        if (btnPressed == DIREC_RIGHT)
        {
            buttonPressed = DIREC_STRAIGHT;            
            break;
        }
        osDelay(SNAKE_WAIT_TIME);
    }
    OledFillScreen(0x00);
    InitSnake();
}
/*游戏运行*/
static void RunSnake(void)
{
    PutFood();
    SnakeMove();

    if (isSnakeDead()) //结束游戏
    {
        GameOver();
    }

    if (Snake.Long == SNAKE_MAX_LONG) //通关
    {
        GamePass();
    }

    //控制游戏难度
    if (Food.Yes == 0){
        osDelay(SNAKE_REFRESH_TIME /(Snake.Level));  
    }
}

static void snakeTask(void *arg)
{
    (void)arg;

    OledInit();

    OledFillScreen(0x00);    
    //***********游戏开始********************//
    InitSnake();
    while (1)
    { 
        RunSnake();
    }
}

/*B按键响应函数, 确认，右移动*/
static void OnButtonBPressed(char *arg)
{
    (void)arg;
    IoTGpioUnregisterIsrFunc(IOT_IO_NAME_GPIO_8);
    
    printf("--OnButtonBPressed--B-\n");
    buttonPressed = DIREC_RIGHT;

    IoTGpioRegisterIsrFunc(IOT_IO_NAME_GPIO_8, IOT_INT_TYPE_EDGE, IOT_GPIO_EDGE_FALL_LEVEL_LOW,
                           OnButtonBPressed, NULL);
}
/*oled 板  A按键响应函数, 选择 左移动*/
static void OnButtonAPressed(char *arg)
{
    (void)arg;
    IoTGpioUnregisterIsrFunc(IOT_IO_NAME_GPIO_5);
    printf("--OnButtonAPressed--A-\n");
    buttonPressed = DIREC_LEFT;        

    IoTGpioRegisterIsrFunc(IOT_IO_NAME_GPIO_5, IOT_INT_TYPE_EDGE, IOT_GPIO_EDGE_FALL_LEVEL_LOW,
                           OnButtonAPressed, NULL);
}

static void snakeDemo(void)
{
    osThreadAttr_t attr;

    IoTGpioInit(IOT_IO_NAME_GPIO_8); //button  按键B
    IoTGpioSetDir(IOT_IO_NAME_GPIO_8, IOT_GPIO_DIR_IN);

    IoTGpioRegisterIsrFunc(IOT_IO_NAME_GPIO_8, IOT_INT_TYPE_EDGE, IOT_GPIO_EDGE_FALL_LEVEL_LOW, OnButtonBPressed, NULL);

    IoTGpioInit(IOT_IO_NAME_GPIO_5); // oled button  按键A
    IoTGpioSetDir(IOT_IO_NAME_GPIO_5, IOT_GPIO_DIR_IN);

    IoTGpioRegisterIsrFunc(IOT_IO_NAME_GPIO_5, IOT_INT_TYPE_EDGE, IOT_GPIO_EDGE_FALL_LEVEL_LOW, OnButtonAPressed, NULL);

    attr.name = "snakeTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 4096;
    attr.priority = osPriorityNormal;

    if (osThreadNew(snakeTask, NULL, &attr) == NULL)
    {
        printf("[OledDemo] Falied to create OledTask!\n");
    }
    printf("\r\n[OledDemo] Succ to create OledTask!\n");
}

APP_FEATURE_INIT(snakeDemo);
